<?php

/*
Copyright (c) 2006, Gregory Szorc
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    * The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//require_once('Net/URL.php');

/**
 * This class talks with GT's CAS server to process web authentication
 *
 * You first declare an instance of the GT_Authn_CAS class
 *
 * $cas = new GT_Authn_CAS($service = '', $redirect = '');
 *
 * The first parameter is where the client gets redirected to after they visit CAS
 * It defaults to $_SERVER['PHP_SELF'] if not specified
 *
 * The second parameter is where the client gets redirected to after authentication has been completed
 * Normally the second parameter is not needed.  The only advantage of using it is if you want the script
 * to redirect the user to another supplementary login script or if you want the 'ticket' parameter
 * to disappear from the URL
 *
 * Example usage:
 *
 * Forcing a user to be logged in to CAS
 * $cas = new GT_Authn_CAS();
 * $cas->forceAuthentication();
 * $GTAccount = $cas->getGTAccount();
 *
 * It is also possible to probe if a user is logged in to CAS.  If they are, you log them in.  If not
 * nothing really happens.
 *
 * $cas = new GT_Authn_CAS();
 * $cas->checkAuthentication();
 *
 * if ($cas->isLoggedIn()) {
 *   echo 'User is logged in.  You can do cool stuff now';
 * }
 * else {
 *   echo 'The user isn't logged in to CAS.  You might want to tell them they get cool feature if they log in';
 * }
 *
 */


class GT_Authn_CAS {
  protected $_service;
  protected $_user;
  protected $_redirectAfter;
  protected $_useSession;

  public function __construct($service = '', $redirectAfter = '', $useSession = true) {
    $this->_useSession = $useSession;
    $this->_redirectAfter = $redirectAfter;

    if ($useSession) {
      @session_start();

      if (!isset($_SESSION['_GT']) || !is_array($_SESSION['_GT'])) {
        $_SESSION['_GT'] = array();
      }

      if (!isset($_SESSION['_GT']['CAS']) || !is_array($_SESSION['_GT']['CAS'])) {
        $_SESSION['_GT']['CAS'] = array();
      }

      $_SESSION['_GT']['CAS']['redirectAfter'] = $redirectAfter;
    }

    if (!$service) {
      $this->_service = strtolower(strtok($_SERVER['SERVER_PROTOCOL'], '/')).'://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'];
      if ($_SERVER['QUERY_STRING']) {
        $get = $_GET;

        unset($get['ticket']);

        if (count($get)) {
          $this->_service .= '?';

          foreach ($get as $k=>$v) {
            if ($v) {
              $this->_service .= "$k=$v&";
            }
            else {
              $this->_service .= $k;
            }
          }

          $this->_service = rtrim($this->_service, '&');

        }

      }
    }
    else {
      $this->_service = $service;
    }

    $this->processEnvironment();

  }

  /**
   * This function checks to see if a user is logged in to CAS
   */
  public function checkAuthentication() {
    if ($this->_useSession) {
      if (!$this->isLoggedIn() && !isset($_SESSION['_GT']['CAS']['checked'])) {
        $_SESSION['_GT']['CAS']['checked'] = true;

        $redir = new Net_URL('https://login.gatech.edu/cas/login');
        $redir->addQueryString('service', $this->_service);
        $redir->addQueryString('gateway', 'true');

        header('Location: '. $redir->getURL());
        exit();
      }
    }
  }

  public function forceAuthentication() {
    if (!$this->isLoggedIn()) {
      $redir = new Net_URL('https://login.gatech.edu/cas/login');
      $redir->addQueryString('service', $this->_service);

      header('Location: '.$redir->getURL());
      exit();
    }
  }


  public function casLogout() {
    unset($_SESSION['_GT']['CAS']); 
    header('Location: https://login.gatech.edu/cas/logout');

  }

  public function localLogout() {
	 unset($_SESSION['_GT']['CAS']);

  }



  public function isLoggedIn() {
    return $this->_user || isset($_SESSION['_GT']['CAS']['user']);
  }

  public function getGTAccount() {
    if ($this->isLoggedIn()) {
      if ($this->_user) return $this->_user;

      if ($this->_useSession) return $_SESSION['_GT']['CAS']['user'];
    }

    return null;
  }

  /**
   * This function looks at the current environment and takes action if necessary
   * Function will see if the CAS 'ticket' variable is present and verify it accordingly
   */
  protected function processEnvironment() {
    if (isset($_GET['ticket'])) {
      if ($user = $this->verifyServiceTicket($_GET['ticket'])) {
        $this->_user = $user;

        if ($this->_useSession) {
          $_SESSION['_GT']['CAS']['user'] = $user;

          if ($_SESSION['_GT']['CAS']['redirectAfter']) {
            header('Location: '.$_SESSION['_GT']['CAS']['redirectAfter']);
            exit();
          }
        }
      }
      else {
        if ($this->_useSession) {
          //ticket was bad for some reason.  Nuke the session information
          $_SESSION['_GT']['CAS'] = array();
        }
      }
    }
  }

  /**
   *  This function could use some work
   */
  public function verifyServiceTicket($ticket) {
    require_once('HTTP/Request.php');

    $req = new HTTP_Request('https://login.gatech.edu/cas/serviceValidate?ticket='.$ticket.'&service='.$this->_service);


    if (!PEAR::isError($req->sendRequest())) {
      $res = $req->getResponseBody();

      $dom = DOMDocument::loadXML($res);
      if (!$dom) return false;

      $success = $dom->getElementsByTagNameNS('http://www.yale.edu/tp/cas', 'authenticationSuccess');
      $failure = $dom->getElementsByTagNameNS('http://www.yale.edu/tp/cas', 'authenticationFailure');

      if ($success->length == 0) {
        return false;
      }

      $user = $dom->getElementsByTagNameNS('http://www.yale.edu/tp/cas', 'user');
      return $user->item(0)->nodeValue;

    }
    else {
      return false;
    }

  }
}

?>
